﻿using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace ft_module_parser
{
    class a3dfixer
    {
        public List<a3d_val> vals = new List<a3d_val>();
        public List<pv_raw> pv_ac = new List<pv_raw>();
        public List<int> pvwhitelist = new List<int>();
        public List<string> itemblacklist = new List<string>();
        private byte[] KEY = Encoding.UTF8.GetBytes("!OKE!FT!");
        private byte[] IV = Encoding.ASCII.GetBytes("SLOWmareSLOWmare");

        public int getSize(string i)
        {
            StreamReader sr = new StreamReader(i);
            //StreamWriter wr = new StreamWriter(s + "fixed");
            Console.WriteLine(Path.GetFileNameWithoutExtension(i));
            var uid = -1;
            while (sr.Peek() > -1)
            {
                var line = sr.ReadLine();
                if (line.Contains("play_control.size="))
                {
                    var lines = line.Split('=');
                    uid = int.Parse(lines[1]);
                }
            }
            sr.Close();
            return uid;
        }

        public static void FixLoop()
        {

        }

        public static IEnumerable<string> GetFiles(string root, string searchPattern)
        {
            Stack<string> pending = new Stack<string>();
            pending.Push(root);
            while (pending.Count != 0)
            {
                var path = pending.Pop();
                string[] next = null;
                try
                {
                    next = Directory.GetFiles(path, searchPattern);
                }
                catch { }
                if (next != null && next.Length != 0)
                    foreach (var file in next) yield return file;
                try
                {
                    next = Directory.GetDirectories(path);
                    foreach (var subdir in next) pending.Push(subdir);
                }
                catch { }
            }
        }

        public void serialize(string s)
        {
            var files = GetFiles("P:\\a3daa", "*HRC*.a3da");

            foreach (var i in files)
            {
                StreamReader sr = new StreamReader(i);
                //StreamWriter wr = new StreamWriter(s + "fixed");
                Console.WriteLine(Path.GetFileNameWithoutExtension(i));

                while (sr.Peek() > -1)
                {
                    var line = sr.ReadLine();
                    if (line.Contains("uid_name"))
                    {
                        var lines = line.Split('=');
                        var uid = lines[1];
                        string result = "";
                        bool flag = false;
                        for (int c = 0; c < uid.Length; c++) // loop over the complete input
                        {
                            if (Char.IsDigit(uid[c])) //check if the current char is digit
                            {
                                flag = true;
                                result += uid[c];
                            }
                            else
                                if (flag) break; //Stop the loop after the first character
                        }
                        if (result != "")
                        {
                            int pvid = int.Parse(result);
                            //int.Parse(lines[1].Substring(5, 3));
                            Console.WriteLine(lines[1] + " -- " + pvid);
                            bool add = true;
                            if (vals.Where(c => c.value1 == lines[1]).Any())
                                add = false;
                            if (add) vals.Add(new a3d_val(lines[1], pvid));
                        }
                    }
                    //wr.WriteLine(line);
                }
                sr.Close();
            }

            a3dlist al = new a3dlist(vals);
            MemoryStream ms = new MemoryStream();
            using (BsonWriter writer = new BsonWriter(ms))
            {
                JsonSerializer serializer = new JsonSerializer();
                serializer.Serialize(writer, al);
            }

            datapack d = new datapack(ZipTools.Zip(ms.ToArray()), ms.ToArray().Length);

            MemoryStream ms2 = new MemoryStream();
            using (BsonWriter writer = new BsonWriter(ms2))
            {
                JsonSerializer serializer = new JsonSerializer();
                serializer.Serialize(writer, d);
            }

            var zipped = EncryptBytes(ZipTools.Zip(ms2.ToArray()));
            
            File.WriteAllBytes("objhrc", zipped);

        }

        public void fixhrc2(string pv_db)
        {
            StreamReader ssr = new StreamReader("whitelist.txt");
            while (ssr.Peek() > -1)
            {
                try
                {
                    pvwhitelist.Add(int.Parse(ssr.ReadLine().Substring(0, 3)));
                }
                catch
                {
                    ssr.ReadLine();
                }
            }
            ssr.Close();

            ssr = new StreamReader("blacklist.txt");
            while (ssr.Peek() > -1)
            {
                itemblacklist.Add(ssr.ReadLine());
            }
            ssr.Close();

            var sourcezipped = DecryptBytes(File.ReadAllBytes("objhrc"));
            var source = ZipTools.Unzip(sourcezipped);
            
            MemoryStream ms = new MemoryStream(source);
            using (BsonReader reader = new BsonReader(ms))
            {
                JsonSerializer serializer = new JsonSerializer();

                datapack dat = serializer.Deserialize<datapack>(reader);

                byte[] source2 = ZipTools.Unzip(dat.a0);

                MemoryStream ms2 = new MemoryStream(source2);

                using (BsonReader reader2 = new BsonReader(ms2))
                {
                    JsonSerializer serialize2 = new JsonSerializer();
                    var al = serialize2.Deserialize<a3dlist>(reader2);
                    vals = al.vals;
                }
            }

            foreach (var i in vals.AsEnumerable().ToList())
            {
                if (!pvwhitelist.Any(c => c == i.pvid))
                {
                    vals.Remove(i);
                }
                else
                {
                    bool add = true;
                    foreach (var v in itemblacklist)
                    {
                        if (i.value1.Contains(v))
                            add = false;
                    }
                    if (!add)
                        vals.Remove(i);
                }
            }

            foreach (var i in vals)
            {
                Console.WriteLine(i.value1);
            }

            using (StreamReader file = new StreamReader(pv_db))
            {
                while (file.Peek() >= 0)
                {
                    string line = file.ReadLine();
                    string[] line2 = line.Split('.');
                    string[] line3 = line2[0].Split('_');

                    int pvid = int.Parse(line3[1]);

                    int lastDiff = 0;

                    var curRawPv = pv_ac.Where(c => c.pvId == pvid).FirstOrDefault();
                    if (curRawPv == null)
                    {
                        pv_ac.Add(new pv_raw(pvid));
                        curRawPv = pv_ac.Where(c => c.pvId == pvid).FirstOrDefault();
                    }

                    if (line.Contains("pv_item"))
                        curRawPv.has_itempv = true;
                    curRawPv.rawrows.Add(line);
                }
            }

            foreach (var i in pv_ac)
            {
                string header = "pv_" + string.Format("{0:000}", i.pvId);
                var values = vals.Where(c => c.pvid == i.pvId);
                var whitelistcheck = pvwhitelist.Where(c => c == i.pvId).Any();
                if ((i.has_itempv == false) && (whitelistcheck))
                {
                    int counter = 0;
                    foreach (var u in values)
                    {
                        string line = header + ".pv_item." + string.Format("{0:00}", counter) + "=" + u.value1;

                        List<string> newList = new List<string>();

                        string lastLine = null;
                        bool flag = false;

                        foreach (var c in i.rawrows)
                        {
                            if (lastLine == null)
                                lastLine = c;

                            string currentLine = c;

                            if (currentLine.Contains("sabi") && currentLine.Contains(string.Format("{0:000}", i.pvId)) && (!flag))
                            {
                                newList.Add(line);
                                newList.Add(c);
                                flag = true;
                            }
                            else
                            {
                                newList.Add(c);
                            }
                        }

                        i.rawrows.Clear();
                        foreach (var c in newList)
                            i.rawrows.Add(c);

                        counter++;
                    }
                }
                //i.rawrows = i.rawrows.OrderBy(c => c.Split('.')[1]).ToList();
            }
            using (StreamWriter sw = new StreamWriter(@"pv_db.txt"))
            {
                foreach (var i in pv_ac)
                {
                    foreach (var u in i.rawrows)
                        sw.WriteLine(u);
                }
            }

        }

        public void fixhrc(string s, string pv_db)
        {
            StreamReader ssr = new StreamReader("whitelist.txt");
            while (ssr.Peek() > -1)
            {
                try
                {
                    pvwhitelist.Add(int.Parse(ssr.ReadLine().Substring(0,3)));
                }
                catch
                {
                    ssr.ReadLine();
                }
            }
            ssr.Close();

            ssr = new StreamReader("blacklist.txt");
            while (ssr.Peek() > -1)
            {
                itemblacklist.Add(ssr.ReadLine());
            }
            ssr.Close();

            var files = GetFiles("P:\\a3daa", "*HRC*.a3da");

            foreach (var i in files)
            {
                StreamReader sr = new StreamReader(i);
                //StreamWriter wr = new StreamWriter(s + "fixed");
                Console.WriteLine(Path.GetFileNameWithoutExtension(i));

                while (sr.Peek() > -1)
                {
                    var line = sr.ReadLine();
                    if (line.Contains("uid_name"))
                    {
                        var lines = line.Split('=');
                        var uid = lines[1];
                        string result = "";
                        bool flag = false;
                        for (int c = 0; c < uid.Length; c++) // loop over the complete input
                        {
                            if (Char.IsDigit(uid[c])) //check if the current char is digit
                            {
                                flag = true;
                                result += uid[c];
                            }
                            else
                                if (flag) break; //Stop the loop after the first character
                        }
                        if (result != "")
                        {
                            int pvid = int.Parse(result);
                            //int.Parse(lines[1].Substring(5, 3));
                            Console.WriteLine(lines[1] + " -- " + pvid);

                            bool add = true;

                            if (vals.Where(c => c.value1 == lines[1]).Any())
                                add = false;
                            
                            foreach (var v in itemblacklist)
                            {
                                if (lines[1].Contains(v))
                                    add = false;
                            }

                            if (add) vals.Add(new a3d_val(lines[1], pvid));
                        }
                    }
                    //wr.WriteLine(line);
                }

                sr.Close();
                //wr.Close();
            }

            using (StreamReader file = new StreamReader(pv_db))
            {
                while (file.Peek() >= 0)
                {
                    string line = file.ReadLine();
                    string[] line2 = line.Split('.');
                    string[] line3 = line2[0].Split('_');

                    int pvid = int.Parse(line3[1]);

                    int lastDiff = 0;

                    var curRawPv = pv_ac.Where(c => c.pvId == pvid).FirstOrDefault();
                    if (curRawPv == null)
                    {
                        pv_ac.Add(new pv_raw(pvid));
                        curRawPv = pv_ac.Where(c => c.pvId == pvid).FirstOrDefault();
                    }

                    if (line.Contains("pv_item"))
                        curRawPv.has_itempv = true;
                    curRawPv.rawrows.Add(line);
                }
            }

            foreach (var i in pv_ac)
            {
                string header = "pv_" + string.Format("{0:000}", i.pvId);
                var values = vals.Where(c => c.pvid == i.pvId);
                var whitelistcheck = pvwhitelist.Where(c => c == i.pvId).Any();
                if ((i.has_itempv == false) && (whitelistcheck))
                {
                    int counter = 0;
                    foreach (var u in values)
                    {
                        string line = header + ".pv_item." + string.Format("{0:00}", counter) + "=" + u.value1;

                        List<string> newList = new List<string>();

                        string lastLine = null;
                        bool flag = false;

                        foreach (var c in i.rawrows)
                        {
                            if (lastLine == null)
                                lastLine = c;

                            string currentLine = c;

                            if (currentLine.Contains("sabi") && currentLine.Contains(string.Format("{0:000}", i.pvId)) && (!flag))
                            {
                                newList.Add(line);
                                newList.Add(c);
                                flag = true;
                            } else
                            {
                                newList.Add(c);
                            }
                        }

                        i.rawrows.Clear();
                        foreach (var c in newList)
                            i.rawrows.Add(c);

                        counter++;
                    }
                }
                //i.rawrows = i.rawrows.OrderBy(c => c.Split('.')[1]).ToList();
            }
            using (StreamWriter sw = new StreamWriter(@"P:\pdiva_test\rom\pv_db.txt"))
            {
                foreach (var i in pv_ac)
                {
                    foreach (var u in i.rawrows)
                        sw.WriteLine(u);
                }
            }
        }

        byte[] EncryptBytes(IEnumerable<byte> bytes)
        {
            //The ICryptoTransform is created for each call to this method as the MSDN documentation indicates that the public methods may not be thread-safe and so we cannot hold a static reference to an instance
            using (var r = Rijndael.Create())
            {
                using (var encryptor = r.CreateEncryptor(KEY, IV))
                {
                    return Transform(bytes, encryptor);
                }
            }
        }

        byte[] DecryptBytes(IEnumerable<byte> bytes)
        {
            //The ICryptoTransform is created for each call to this method as the MSDN documentation indicates that the public methods may not be thread-safe and so we cannot hold a static reference to an instance
            using (var r = Rijndael.Create())
            {
                using (var decryptor = r.CreateDecryptor(KEY, IV))
                {
                    return Transform(bytes, decryptor);
                }
            }
        }

        byte[] Transform(IEnumerable<byte> bytes, ICryptoTransform transform)
        {
            using (var stream = new MemoryStream())
            {
                using (var cryptoStream = new CryptoStream(stream, transform, CryptoStreamMode.Write))
                {
                    foreach (var b in bytes)
                        cryptoStream.WriteByte(b);
                }

                return stream.ToArray();
            }
        }
    }
}
